package com.doctcloud.common.core.utils;

import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Map;

/**
 * 增强增强版编码转换工具类
 * 支持：
 * 1. Oracle ZHS16GBK → UTF-8
 * 2. Oracle US7ASCII → UTF-8
 * 适用场景：解决从Oracle数据库读取数据时的中文乱码问题
 */
public class EncodingConverter {

    /**
     * 原始编码类型枚举
     * 对应Oracle数据库常见编码格式
     */
    public enum SourceEncoding {
        GBK,         // Oracle ZHS16GBK编码（兼容标准GBK）
        US7ASCII     // Oracle US7ASCII编码（ASCII扩展）
    }

    // 字符集常量定义（使用Charset确保线程安全）
    private static final Charset GBK_CHARSET = Charset.forName("GBK");
    private static final Charset US7ASCII_CHARSET = Charset.forName("US-ASCII");
    private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");

    /**
     * 通用转换入口：将对象中所有字符串字段从指定编码转为UTF-8
     * @param obj 待转换对象（支持单对象、集合、数组、嵌套对象）
     * @param sourceEncoding 原始编码类型（不可为null）
     */
    public static void convertToUtf8(Object obj, SourceEncoding sourceEncoding) {
        if (obj == null || sourceEncoding == null) {
            return;
        }
        // 使用IdentityHashMap跟踪已处理对象，防止循环引用导致栈溢出
        convert(obj, new IdentityHashMap<>(), sourceEncoding);
    }

    /**
     * 快捷方法：GBK编码字符串转UTF-8
     *
     * @param obj             待转换对象
     */
    public static void gbkToUtf8( Object obj ) {
        convertToUtf8(obj, SourceEncoding.GBK);
    }


    /**
     * 快捷方法：US7ASCII编码字符串转UTF-8
     * @param obj 待转换对象
     */
    public static void us7AsciiToUtf8(Object obj) {
        convertToUtf8(obj, SourceEncoding.US7ASCII);
    }

    /**
     * 递归处理所有对象的字符串字段
     * @param obj 待转换对象
     * @param visited 已处理对象集合（防循环引用）
     * @param sourceEncoding 原始编码类型
     */
    private static void convert(Object obj, Map<Object, Boolean> visited, SourceEncoding sourceEncoding) {
        // 终止条件：对象为null或已处理过
        if (obj == null || visited.containsKey(obj)) {
            return;
        }
        visited.put(obj, true);

        Class<?> clazz = obj.getClass();

        // 处理集合类型（List/Set等）
        if (obj instanceof Collection) {
            Collection<?> collection = (Collection<?>) obj;
            for (Object item : collection) {
                convert(item, visited, sourceEncoding);
            }
            return;
        }

        // 处理数组类型（包括基本类型数组和对象数组）
        if (clazz.isArray()) {
            int length = java.lang.reflect.Array.getLength(obj);
            for (int i = 0; i < length; i++) {
                Object arrayElement = java.lang.reflect.Array.get(obj, i);
                convert(arrayElement, visited, sourceEncoding);
            }
            return;
        }

        // 基本类型、枚举、字符串无需转换（字符串在字段处理中单独转换）
        if (clazz.isPrimitive() || clazz.isEnum() || clazz == String.class) {
            return;
        }

        // 处理普通对象的字段
        processObjectFields(obj, visited, sourceEncoding, clazz);
    }

    /**
     * 处理对象的所有字段，转换字符串类型字段的编码
     */
    private static void processObjectFields(Object obj, Map<Object, Boolean> visited,
                                            SourceEncoding sourceEncoding, Class<?> clazz) {
        // 获取所有声明的字段（包括私有字段）
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 跳过静态字段和常量字段（final）
            if (java.lang.reflect.Modifier.isStatic(field.getModifiers())
                    || java.lang.reflect.Modifier.isFinal(field.getModifiers())) {
                continue;
            }

            try {
                field.setAccessible(true); // 突破访问权限限制
                Object fieldValue = field.get(obj);

                // 处理字符串字段：根据原始编码转换为UTF-8
                if (fieldValue instanceof String) {
                    String convertedStr = convertString((String) fieldValue, sourceEncoding);
                    field.set(obj, convertedStr);
                }
                // 处理嵌套对象：递归转换
                else {
                    convert(fieldValue, visited, sourceEncoding);
                }
            } catch (IllegalAccessException e) {
                // 打印异常但不中断，避免单个字段处理失败影响整体
                System.err.println("处理字段[" + field.getName() + "]时发生错误：" + e.getMessage());
            }
        }

        // 处理父类字段（递归向上查找）
        Class<?> superClass = clazz.getSuperclass();
        if (superClass != null && superClass != Object.class) {
            processObjectFields(obj, visited, sourceEncoding, superClass);
        }
    }

    /**
     * 核心转换逻辑：将字符串从指定编码转为UTF-8
     * @param originalStr 原始字符串
     * @param sourceEncoding 原始编码类型
     * @return 转换后的UTF-8字符串
     */
    private static String convertString(String originalStr, SourceEncoding sourceEncoding) {
        if (originalStr == null || originalStr.isEmpty()) {
            return originalStr;
        }

        try {
            // 根据原始编码获取字节数组，再用UTF-8解码
            byte[] bytes;
            switch (sourceEncoding) {
                case GBK:
                    bytes = originalStr.getBytes(GBK_CHARSET);
                    break;
                case US7ASCII:
                    bytes = originalStr.getBytes(US7ASCII_CHARSET);
                    break;
                default:
                    return originalStr;
            }
            return new String(bytes, UTF8_CHARSET);
        } catch (Exception e) {
            // 转换失败时返回原始字符串，避免数据丢失
            System.err.println("字符串编码转换失败：" + e.getMessage());
            return originalStr;
        }
    }
}